package com.bluexmicro.bluetooth.peripheral.handler

import android.annotation.SuppressLint
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothStatusCodes
import android.os.Build
import com.bluexmicro.bluetooth.LogUtils
import com.bluexmicro.bluetooth.bluetoothStatusCodesName
import com.bluexmicro.bluetooth.model.GattStatus
import com.bluexmicro.bluetooth.toHexString
import kotlinx.coroutines.CancellableContinuation
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

typealias WriteDescriptorCallback = (
    gatt: BluetoothGatt,
    descriptor: BluetoothGattDescriptor,
    status: Int
) -> Unit

class WriteDescriptorHandler {

    private var callback: WriteDescriptorCallback? = null
    private var cancellableContinuation: CancellableContinuation<Result<Any>>? = null

    fun onDescriptorWrite(
        gatt: BluetoothGatt,
        descriptor: BluetoothGattDescriptor,
        status: Int
    ) {
        callback?.invoke(gatt, descriptor, status)
    }

    private fun setCallback(cb: WriteDescriptorCallback) {
        this.callback = cb
    }

    fun isActive(): Boolean {
        return cancellableContinuation?.isActive == true
    }

    @SuppressLint("MissingPermission")
    suspend fun execute(
        gatt: BluetoothGatt,
        descriptor: BluetoothGattDescriptor,
        value: ByteArray
    ) = suspendCancellableCoroutine<Result<Any>> {
        cancellableContinuation = it

        setCallback { _, d, status ->
            if (d.uuid.equals(descriptor.uuid)) {
                this.callback = null
                if (it.isActive) {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        it.resume(Result.success(Unit))
                    } else {
                        val reason = GattStatus.fromValue(status).name
                        it.resume(Result.failure(IllegalStateException("WriteDescriptor response failed, gatt status: $status, reason: $reason")))
                    }
                }
            }
        }

        if (it.isActive) {
            val res: Boolean
            val detail: String
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                val code = gatt.writeDescriptor(descriptor, value)
                res = code == BluetoothStatusCodes.SUCCESS
                detail = code.bluetoothStatusCodesName()
            } else {
                descriptor.value = value
                res = gatt.writeDescriptor(descriptor)
                detail = if (res) "done" else "failed"
            }
            LogUtils.sendLog(
                mapOf(
                    "mac" to gatt.device.address,
                    "call method" to "writeDescriptor",
                    "value" to value.toHexString(),
                    "detail" to detail
                )
            )
            if (!res) {
                this.callback = null
                it.resume(Result.failure(Throwable("WriteDescriptor failed")))
            }
            it.invokeOnCancellation {
                this.callback = null
            }
        }
    }
}